//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  The core libraries for the MapWindow 6.0 project.
//
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either expressed or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is MapWindow.dll for the MapWindow 6.0 project
//
// The Initial Developer of this Original Code is Ted Dunsford. Created in September, 2007.
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************
using System.Collections.Generic;
using MapWindow.Components;
using MapWindow.Data;
using MapWindow.Drawing;
using MapWindow.Geometries;
using MapWindow.Main;
using System.Drawing;

namespace MapWindow.DirectX
{
    /// <summary>
    /// The only difference between an EventList&lt;IMapLayer&gt; and this collection is that
    /// this provides methods that allow access by an object Key, and will pass 
    /// </summary>
    public class LayerCollectionDX: LayerCollection, ILayerCollectionDX
    {
        


       
        #region Private Variables

 
        private bool _pointsValid;       
        private bool _lineStringsValid;        
        private bool _polygonsValid;

        private List<LineLayerDX> _lineLayers;
        private List<PointLayerDX> _pointLayers;    
        private List<PolygonLayerDX> _polygonLayers;

        private bool _isInitialized; // True if layers already existed before a member change
        private IProgressHandler _progressHandler;


        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new blank instance of a MapLayer collection.  This is especially useful
        /// for tracking layers that can draw themselves.  This does not concern itself with
        /// view extents like a dataframe, but rather is a grouping of layers that is itself
        /// also an IMapLayer.
        /// </summary>
        public LayerCollectionDX(IMapFrameDX containerFrame)
        {
            base.MapFrame = containerFrame;
            base.ParentGroup = containerFrame;
            Configure();
        }

        /// <summary>
        /// Creates a new LayerCollection that belongs in a sub-group.
        /// </summary>
        /// <param name="frame"></param>
        /// <param name="group"></param>
        public LayerCollectionDX(IMapFrameDX frame, IGroupDX group)
        {
            base.MapFrame = frame;
            base.ParentGroup = group;
        }

        /// <summary>
        /// Creates a new layer collection that is free-floating.  This will not be contained in a map frame.
        /// </summary>
        public LayerCollectionDX()
        {
            Configure();
        }

        private void Configure()
        {
            _progressHandler = MapWindow.DirectX.Components.LayerManagerDX.DefaultLayerManager.ProgressHandler;
        }

        /// <summary>
        /// Gets the map frame of this layer collection
        /// </summary>
        public new IMapFrameDX MapFrame
        {
            get { return base.MapFrame as IMapFrameDX; }
            // not sure I want to try to implement set.  That would force me to change the _mapFrame property on all the layers?
            // Maybe layers don't acces
        }
        IFrame ILayerCollection.MapFrame
        {
            get { return base.MapFrame; }
        }



        #endregion

        #region Methods

        #region Add


        /// <summary>
        /// This overload automatically constructs a new MapLayer from the specified
        /// feature layer with the default drawing characteristics and returns a valid
        /// IMapLayer which can be further cast into a PointLayer, MapLineLayer or
        /// a PolygonLayer, depending on the data that is passed in.
        /// </summary>
        /// <param name="layer">A pre-exsiting FeatureLayer that has already been created from a featureSet</param>
        public void Add(ILayerDX layer)
        {
            base.Add(layer);

        }

        /// <summary>
        /// Adds the elements of the specified collection to the end of the System.Collections.Generic.List&lt;T&gt;
        /// </summary>
        /// <param name="collection">collection: The collection whose elements should be added to the end of the
        /// System.Collections.Generic.List&lt;T&gt;. The collection itself cannot be null, but it can contain elements that are null, 
        /// if type T is a reference type.</param>
        /// <exception cref="System.ApplicationException">Unable to add while the ReadOnly property is set to true.</exception>
        public void AddRange(IEnumerable<ILayerDX> collection)
        {
            foreach (ILayerDX layer in collection)
            {
                base.Add(layer);
            }
        }


        /// <summary>
        /// Adds the specified filename to the map as a new layer.
        /// </summary>
        /// <param name="filename">The string filename to add as a layer.</param>
        /// <returns>An IMapLayer that is the layer handle for the specified file.</returns>
        public virtual ILayerDX Add(string filename)
        {
            IDataSet dataSet = DataManager.DefaultDataManager.OpenFile(filename);
            return Add(dataSet);
        }

        /// <summary>
        /// Adds the dataset specified to the file.  Depending on whether this is a featureSet,
        /// Raster, or ImageData, this will return the appropriate layer for the map.
        /// </summary>
        /// <param name="dataSet">A dataset</param>
        /// <returns>The IMapLayer to add</returns>
        public virtual ILayerDX Add(IDataSet dataSet)
        {
            IFeatureSet fs = dataSet as IFeatureSet;
            if (fs != null) return Add(fs);
            IRaster r = dataSet as IRaster;
            if (r != null) return Add(r);
            //IImageData id = dataSet as IImageData;
            //if (id != null) return Add(id);
            return null;
        }

        
        /// <summary>
        /// This overload automatically constructs a new MapLayer from the specified
        /// feature layer with the default drawing characteristics and returns a valid
        /// IMapLayer which can be further cast into a PointLayer, MapLineLayer or
        /// a PolygonLayer, depending on the data that is passed in.
        /// </summary>
        /// <param name="featureSet">Any valid IFeatureSet that does not yet have drawing characteristics</param>
        /// <returns>A newly created valid implementation of FeatureLayer which at least gives a few more common
        /// drawing related methods and can also be cast into the appropriate Point, Line or Polygon layer.</returns>
        public virtual IFeatureLayerDX Add(IFeatureSet featureSet)
        {
            return Add(featureSet, _progressHandler);
        }

        /// <summary>
        /// This overload automatically constructs a new MapLayer from the specified
        /// feature layer with the default drawing characteristics and returns a valid
        /// IMapLayer which can be further cast into a PointLayer, MapLineLayer or
        /// a PolygonLayer, depending on the data that is passed in.
        /// </summary>
        /// <param name="featureSet">Any valid IFeatureSet that does not yet have drawing characteristics</param>
        /// <param name="progressHandler">Any valid implementation of the IProgressHandler interface for progress messages</param>
        /// <returns>A newly created valid FeatureLayer which at least gives a few more common
        /// drawing related methods and can also be cast into the appropriate Point, Line or Polygon layer.</returns>
        public virtual IFeatureLayerDX Add(IFeatureSet featureSet, IProgressHandler progressHandler)
        {
            if (featureSet == null) return null;
            if (featureSet.FeatureType == FeatureTypes.Point)
            {
                PointLayerDX pl = new PointLayerDX(featureSet, progressHandler);
                base.Add(pl);
                return pl;
            }
            else if (featureSet.FeatureType == FeatureTypes.Line)
            {
                LineLayerDX ll = new LineLayerDX(featureSet);
                base.Add(ll);
                return ll;
            }
            else if (featureSet.FeatureType == FeatureTypes.Polygon)
            {
                PolygonLayerDX pl = new PolygonLayerDX(featureSet);
                base.Add(pl);
                return pl;
            }

            return null;
            //throw new NotImplementedException("Right now only point types are supported.");
        }


        /// <summary>
        /// Adds a new raster layer to the map, and returns the newly created layer as a RasterLayerDX.
        /// </summary>
        /// <param name="raster">The IRaster showing grid values that should be treated as 3D terrain.</param>
        /// <returns>The newly created RasterLayerDX that can render terrain in 3D</returns>
        public virtual RasterLayerDX Add(IRaster raster)
        {
            RasterLayerDX result = new RasterLayerDX(raster, this);
            return result;
        }

        /// <summary>
        /// Adds a enw raster layer to the map, and specifies a very specific texture to use
        /// instead of one generated from the cell values of the raster.
        /// </summary>
        /// <param name="raster">The raster to use</param>
        /// <param name="texture">The texture to drape on top of this raster image.</param>
        /// <returns>A new RasterLayerDX</returns>
        public virtual RasterLayerDX Add(IRaster raster, Image texture)
        {
            RasterLayerDX result = new RasterLayerDX(raster, texture, this);
            return result;
        }

        /// <summary>
        /// Adds a enw raster layer to the map, and specifies a very specific texture to use
        /// instead of one generated from the cell values of the raster.
        /// </summary>
        /// <param name="raster">The raster to use</param>
        /// <param name="texture">The texture to drape on top of this raster image.</param>
        /// <param name="progressHandler">The progress handler for progress messages</param>
        /// <returns>A new RasterLayerDX</returns>
        public virtual RasterLayerDX Add(IRaster raster, Image texture, IProgressHandler progressHandler)
        {
            RasterLayerDX result = new RasterLayerDX(raster, texture, this, progressHandler);
            return result;
        }

        #endregion



        /// <summary>
        /// Returns all the layers with point features in a list of PointLayers
        /// returns the values already cast as PointLayers.
        /// </summary>
        public virtual List<PointLayerDX> GetPointLayers()
        {
            if (_pointsValid == false)
            {
                RecountPoints();
            }
            return _pointLayers;
        }

        /// <summary>
        /// Returns the N'th PointLayer where N is the index where only point layers are counted
        /// </summary>
        /// <param name="N"></param>
        /// <returns></returns>
        public virtual PointLayerDX GetPointLayer(int N)
        {
            if (_pointsValid == false) RecountPoints();
            return _pointLayers[N];
        }


        /// <summary>
        /// Returns a list of LineLayer interfaces representing each of the current MapLayers that happens
        /// to specifically be a LineStringLayer.  The index values will probably not match the index value in 
        /// the complete list of layers.
        /// </summary>
        public virtual List<LineLayerDX> GetLineStringLayers()
        {
            if (_lineStringsValid == false) RecountLineStrings();
            return _lineLayers;
        }

        /// <summary>
        /// Returns the N'th LineStringLayer where N is the index if only LineString layers are counted
        /// </summary>
        /// <param name="N">The integer index specifying the LineString Layer</param>
        /// <returns></returns>
        public virtual LineLayerDX GetLineStringLayer(int N)
        {
            if (_lineStringsValid == false) RecountLineStrings();
            return _lineLayers[N];
        }

        /// <summary>
        /// Returns a list of PolygonLayer interfaces representing each of the current MapLayers that happens
        /// to specifically be a LineStringLayer.  The index values will probably not match the index value in 
        /// the complete list of layers.
        /// </summary>
        public virtual List<PolygonLayerDX> GetPolygonLayers()
        {
            if (_polygonsValid == false) RecountPolygons();
            return _polygonLayers;
        }

        /// <summary>
        /// Returns the N'th PolygonLayer where N is the index if only LineString layers are counted
        /// </summary>
        /// <param name="N">The integer index specifying the LineString Layer</param>
        /// <returns></returns>
        public virtual PolygonLayerDX GetPolygonLayer(int N)
        {
            if (_polygonsValid == false) RecountPolygons();
            return _polygonLayers[N];
        }



           


        #endregion

        #region Properties

        /// <summary>
        /// Gets the count of all the items
        /// </summary>
        public new int Count
        {
            get
            {
                return base.Count;
            }
        }


      
        

        /// <summary>
        /// Gets a list of LineLayer interfaces representing each of the current MapLayers that happens
        /// to specifically be a LineStringLayer.  The index values will probably not match the index value in 
        /// the complete list of layers.
        /// </summary>
        public virtual List<LineLayerDX> LineStringLayers
        {
            get
            {
                List<LineLayerDX> result = new List<LineLayerDX>();
                foreach (ILayerDX ml in this)
                {
                    if (ml is LineLayerDX)
                    {
                        result.Add((LineLayerDX)ml);
                    }
                }
                return result;
            }
        }


        /// <summary>
        /// Gets the total count of Point layers in this collection
        /// </summary>
        public virtual int NumPointLayers
        {
            get
            {
                if (_pointsValid == false) RecountPoints();
                return _pointLayers.Count;
            }
        }

        /// <summary>
        /// Gets the total count of LineString layers in this collection
        /// </summary>
        public virtual int NumLineLayers
        {
            get
            {
                if (_lineStringsValid == false) RecountLineStrings();
                return _lineLayers.Count;
            }
        }

        /// <summary>
        /// Gets the total count of Polygon layers in this collection
        /// </summary>
        public virtual int NumPolygonLayers
        {
            get
            {
                if (_polygonsValid == false) RecountPolygons();
                return _polygonLayers.Count;
            }
        }

        /// <summary>
        /// Essentially filters the list of all layers and only returns those that have point features, and
        /// returns interfaces that are already cast as PointLayers.
        /// </summary>
        public virtual List<PointLayerDX> PointLayers
        {
            get
            {
                List<PointLayerDX> result = new List<PointLayerDX>();
                foreach (ILayerDX ml in this)
                {
                    if (ml is PointLayerDX)
                    {
                        result.Add((PointLayerDX)ml);
                    }
                }
                return result;
            }
        }

        /// <summary>
        /// Essentially filters the list of all layers and only returns those that have point features, and
        /// returns interfaces that are already cast as PolygonLayers.
        /// </summary>
        public virtual List<PolygonLayerDX> PolygonLayers
        {
            get
            {
                List<PolygonLayerDX> result = new List<PolygonLayerDX>();
                foreach (ILayerDX ml in this)
                {
                    if (ml is PolygonLayerDX)
                    {
                        result.Add((PolygonLayerDX)ml);
                    }
                }
                return result;
            }
        }

        /// <summary>
        /// Gets or sets the progress handler to use for methods that don't specifically specify 
        /// a progress handler.  By default, this will load the default layer manager progress handler.
        /// </summary>
        public IProgressHandler ProgressHandler
        {
            get { return _progressHandler; }
            set { _progressHandler = value; }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public new ILayerDX this[int index]
        {
            get { return base[index] as ILayerDX; }
            set { base[index] = value; }
        }

        /// <summary>
        /// Gets a new enumerator specifically for the ILayerDX layers in the collection.
        /// This will skip any layers that don't satisfy the criteria of implementing IlayerDX.
        /// </summary>
        /// <returns>An IEnumerator of ILayerDX</returns>
        public new IEnumerator<ILayerDX> GetEnumerator()
        {
            return new LayerEnumeratorDX(base.GetEnumerator());
        }

        #endregion


       


        /// <summary>
        /// When changes are made to the master list, these lists are invalidated so that if they are
        /// queried again, they will have to be accessed by checking the master list instead of the cache
        /// </summary>
        private void RecountLineStrings()
        {
            _lineLayers = new List<LineLayerDX>();
            foreach (ILayerDX ml in this)
            {
                if (ml is LineLayerDX)
                {
                    _lineLayers.Add((LineLayerDX)ml);
                }
            }
            _lineStringsValid = true;
        }

        /// <summary>
        /// When changes are made to the master list, these lists are invalidated so that if they are
        /// queried again, they will have to be accessed by checking the master list instead of the cache
        /// </summary>
        private void RecountPoints()
        {
            
            _pointLayers = new List<PointLayerDX>();
            foreach (ILayer ml in this)
            {
                if (ml is PointLayerDX)
                {
                    _pointLayers.Add((PointLayerDX)ml);
                }
            }
            _pointsValid = true;
        }

        /// <summary>
        /// Recounts the Poylgon layers to re-validate the cached list
        /// </summary>
        private void RecountPolygons()
        {
            _polygonLayers = new List<PolygonLayerDX>();
            foreach (ILayer ml in this)
            {
                if (ml is PolygonLayerDX)
                {
                    _polygonLayers.Add((PolygonLayerDX)ml);
                }
            }
            _polygonsValid = true;
        }

        /// <summary>
        /// Fires the ItemChanged event and the MembersChanged event and resets any cached lists
        /// </summary>
        protected virtual void OnMembersChanged()
        {
            if (EventsSuspended)
            {
                base.OnListChanged();
                return;
            }
            if (_isInitialized == false)
            {
                if (Count > 0)
                {
                    OnZoomToLayer(this[0], new EnvelopeArgs(this[0].Envelope));
                    _isInitialized = true;
                }
            }
            else
            {
                if (Count == 0)
                {
                    _isInitialized = false;
                }
            }
           
            // reset cached extra lists if any
            _pointLayers = null;
            _lineLayers = null;
            _polygonLayers = null;
            _pointsValid = false;
            _lineStringsValid = false;
            _polygonsValid = false;
            base.OnListChanged();
        }

       

        private void Layer_ZoomToLayer(object sender, EnvelopeArgs e)
        {
            OnZoomToLayer(sender, e);
        }

      

       
      

      
       
       

    }
}
